VERSION 1.0 CLASS
BEGIN
  MultiUse = -1  'True
  Persistable = 0  'NotPersistable
  DataBindingBehavior = 0  'vbNone
  DataSourceBehavior  = 0  'vbNone
  MTSTransactionMode  = 0  'NotAnMTSObject
END
Attribute VB_Name = "StringDifference"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = True
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = True

'
'Copyright (c) 2010 Andrew Wall
'
' http://www.opensource.org/licenses/mit-license.php
'
'Permission is hereby granted, free of charge, to any person obtaining a copy
'of this software and associated documentation files (the "Software"), to deal
'in the Software without restriction, including without limitation the rights
'to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
'copies of the Software, and to permit persons to whom the Software is
'furnished to do so, subject to the following conditions:
'
'The above copyright notice and this permission notice shall be included in
'all copies or substantial portions of the Software.
'
'THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
'IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
'FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
'AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
'LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
'OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
'THE SOFTWARE.
'

Option Explicit

Private mResult As Boolean
Private mMessage As String
Private mMaxLength As Integer
Private mStrMessage As New StringMessage

Private mDisplay As Aperture
Private mDisplay2 As Aperture
Private mCursor As Aperture
Private mCursor2 As Aperture
Private mFirst As Integer

Public Sub init(maxLength As Integer)
mMaxLength = maxLength
End Sub

Public Sub compare(LHS As String, RHS As String)
mResult = (LHS = RHS)
If formattedMessageRequired(mResult, LHS, RHS) Then
    mMessage = generateFormattedMessage(LHS, RHS)
Else
    mMessage = generateLimitedMessage(mResult, LHS, RHS)
End If
End Sub

Public Property Get result() As Boolean
result = mResult
End Property

Public Property Get message() As String
'If Len(mMessage) = 0 Then
'    mMessage = generateDifferenceMessage()
'End If
message = mMessage
End Property


Private Function formattedMessageRequired(result As Boolean, LHS As String, RHS As String) As Boolean
If result Then
    Exit Function
End If
Dim shortest As Integer
Dim other As Integer
shortest = Len(LHS)
other = Len(RHS)
If shortest > other Then
    shortest = other
End If
If shortest < mMaxLength Then
    Exit Function
End If

mFirst = mStrMessage.firstDifferenceIndex(LHS, RHS)

formattedMessageRequired = Not displayPosLessThanCursorMinimum(mFirst)
End Function

Private Function generateFormattedMessage(LHS As String, RHS As String) As String
Dim cursor As Integer
Dim cursorOffset As Integer
Dim title2 As String
Dim param As String
Dim message As String
If displayPosLessThanCursorMaximum(mFirst) Then
    cursor = mDisplay.LeftPos + mFirst
Else
    cursor = mCursor.LeftPos + mCursor.width
End If
cursorOffset = cursor - mDisplay.LeftPos
message = mDisplay.Title + "<" + generateDoubleLimitedString(LHS, mFirst - cursorOffset + 1, mDisplay.width) + ">" + vbCrLf
message = message + mDisplay2.Title + "<" + generateDoubleLimitedString(RHS, mFirst - cursorOffset + 1, mDisplay.width) + ">" + vbCrLf
message = message + mCursor.Title + Space$(cursor - mCursor.LeftPos) + "^" + vbCrLf
title2 = mCursor2.Title
title2 = title2 + String(Len(mCursor.Title) - Len(mCursor2.Title), "-")
title2 = title2 + String(cursor - mCursor.LeftPos, "-")
param = CStr(mFirst) + " "
Mid(title2, Len(mCursor2.Title) + 1, Len(param)) = param
message = message + title2 + "|"
'Debug.Print message
generateFormattedMessage = message
End Function
Private Function displayPosLessThanCursorMinimum(pos As Integer) As Boolean
displayPosLessThanCursorMinimum = (pos + mDisplay.LeftPos) < mCursor.LeftPos
End Function
Private Function displayPosLessThanCursorMaximum(pos As Integer) As Boolean
displayPosLessThanCursorMaximum = (pos + mDisplay.LeftPos) < (mCursor.LeftPos + mCursor.width)
End Function

Private Function generateLimitedMessage(result As Boolean, LHS As String, RHS As String) As String
generateLimitedMessage = mStrMessage.message(result, wrapLimitedString(LHS), wrapLimitedString(RHS))
End Function
'@Note
'wrapString could be a decorator
Private Function wrapLimitedString(value As String) As String
wrapLimitedString = "<" + generateLimitedString(value) + ">"
End Function
'@Note
'generateLimitedString could be a decorator
Private Function generateLimitedString(value As String) As String
If Len(value) > mMaxLength Then
    generateLimitedString = Left$(value, mMaxLength - 3) + "..."
Else
    generateLimitedString = value
End If
End Function
Private Function generateDoubleLimitedString(value As String, leftLimit As Integer, width As Integer) As String
generateDoubleLimitedString = Mid(value, leftLimit, width)
If leftLimit > 1 Then
    Mid(generateDoubleLimitedString, 1, 3) = "..."
End If
If leftLimit + width < Len(value) Then
    Mid(generateDoubleLimitedString, width - 2, 3) = "..."
End If
End Function

Private Sub Class_Initialize()
mMaxLength = 12
mDisplay.Title = "Expected: "
mDisplay.LeftPos = Len(mDisplay.Title)
mDisplay.width = 50
mDisplay2.Title = "but got:  "
mDisplay2.LeftPos = Len(mDisplay.Title)
mDisplay2.width = mDisplay.width
mCursor.Title = "First difference: "
mCursor.LeftPos = Len(mCursor.Title)
mCursor.width = min(mDisplay.LeftPos + mDisplay.width, mCursor.LeftPos + 50) - mCursor.LeftPos - 3
mCursor2.Title = "index "
mCursor2.LeftPos = mCursor.LeftPos
mCursor2.width = mCursor.width


End Sub
